﻿// 2022/4/1: 首个版本
// 2022/4/6: 增加BusRecordFormat.OneChannelPcap
// 2022/11/8: ASC支持存储Flexray数据
// 2023/2/15: 修正注释
// 2023/2/20: 支持远程采集模式
// 2023/4/10: 增加Load/Save方法
// 2023/4/23: 增加RecordLegacyStyle字段
// 2023/5/10: 支持BusChannelType.SomeIP

using System;
using System.Collections.Generic;
using System.Xml;
using System.Linq;
using ASEva;
using ASEva.Utility;

namespace CommonSource
{
    enum SignalIOMode
    {
        NoSignals,
        WithoutBusSignals,
        AllSignals,
    }

    enum BusRecordFormat
    {
        // CAN, CAN-FD, Flexray
        OneChannelAsc = 1,
        MultiChannelAsc = 2,
        MultiTypeAsc = 3,

        // CAN, CAN-FD, LIN, Flexray, Ethernet
        OneChannelBlf = 11,
        MultiChannelBlf = 12,
        MultiTypeBlf = 13,

        // CAN, CAN-FD, LIN
        OneChannelEbd = 21,
        MultiChannelEbd = 22,

        // Ethernet
        OneChannelPcap = 31,
    }

    class BusRecordConfig
    {
        public BusRecordFormat Format { get; set; }
        public bool SmallSegment { get; set; } // true: 6min/file, false: 60min/file；对于MultiType格式，只要有一个类型为true则都为true
    }

    class DefaultFileIOConfig : ModuleConfig
    {
        public bool OnlineWriteBus { get; set; }
        public SignalIOMode OnlineWriteSignal { get; set; }
        public bool OnlineWriteSample { get; set; }
        public bool OnlineWriteMatrix { get; set; }
        public SignalIOMode OfflineWriteSignal { get; set; }
        public bool OfflineWriteSample { get; set; }
        public bool OfflineWriteMatrix { get; set; }
        
        public bool FromRawReadBus { get; set; }
        public bool OfflineFromGenReadBus { get; set; }
        public SignalIOMode OfflineFromGenReadSignal { get; set; }
        public bool OfflineFromGenReadSample { get; set; }
        public bool OfflineFromGenReadMatrix { get; set; }
        public bool ReplayFromGenReadBus { get; set; }
        public SignalIOMode ReplayFromGenReadSignal { get; set; }
        public bool ReplayFromGenReadSample { get; set; }
        public bool ReplayFromGenReadMatrix { get; set; }

        public Dictionary<BusChannelType, BusRecordConfig> BusRecordConfigTable { get; set; }

        public bool RecordLegacyStyle { get; set; }

        private Dictionary<BusRecordFormat, BusChannelType[]> availableRecordFormatTable;
        public Dictionary<BusRecordFormat, BusChannelType[]> AvailableRecordFormatTable
        {
            get { return availableRecordFormatTable; }
        }

        public DefaultFileIOConfig()
        {
            availableRecordFormatTable = new Dictionary<BusRecordFormat, BusChannelType[]>();
            availableRecordFormatTable[BusRecordFormat.OneChannelAsc] = 
                availableRecordFormatTable[BusRecordFormat.MultiChannelAsc] = 
                availableRecordFormatTable[BusRecordFormat.MultiTypeAsc] = new BusChannelType[]
                {
                    BusChannelType.Can,
                    BusChannelType.CanFD,
                    BusChannelType.Flexray,
                };
            availableRecordFormatTable[BusRecordFormat.OneChannelBlf] = 
                availableRecordFormatTable[BusRecordFormat.MultiChannelBlf] = 
                availableRecordFormatTable[BusRecordFormat.MultiTypeBlf] = new BusChannelType[]
                {
                    BusChannelType.Can,
                    BusChannelType.CanFD,
                    BusChannelType.Lin,
                    BusChannelType.Flexray,
                    BusChannelType.Ethernet,
                    BusChannelType.SomeIP,
                };
            availableRecordFormatTable[BusRecordFormat.OneChannelEbd] = 
                availableRecordFormatTable[BusRecordFormat.MultiChannelEbd] = new BusChannelType[]
                {
                    BusChannelType.Can,
                    BusChannelType.CanFD,
                    BusChannelType.Lin,
                };
            availableRecordFormatTable[BusRecordFormat.OneChannelPcap] = new BusChannelType[]
                {
                    BusChannelType.Ethernet,
                    BusChannelType.SomeIP,
                };

            ResetToDefault();
        }

        public void ResetToDefault()
        {
            OnlineWriteBus = true;
            OnlineWriteSignal = SignalIOMode.WithoutBusSignals;
            OnlineWriteSample = true;
            OnlineWriteMatrix = false;
            OfflineWriteSignal = SignalIOMode.WithoutBusSignals;
            OfflineWriteSample = true;
            OfflineWriteMatrix = false;

            FromRawReadBus = true;
            OfflineFromGenReadBus = false;
            OfflineFromGenReadSignal = SignalIOMode.NoSignals;
            OfflineFromGenReadSample = true;
            OfflineFromGenReadMatrix = false;
            ReplayFromGenReadBus = true;
            ReplayFromGenReadSignal = SignalIOMode.AllSignals;
            ReplayFromGenReadSample = true;
            ReplayFromGenReadMatrix = true;

            BusRecordConfigTable = new Dictionary<BusChannelType, BusRecordConfig>();
            BusRecordConfigTable[BusChannelType.Can] = new BusRecordConfig
            {
                Format = BusRecordFormat.MultiTypeAsc,
                SmallSegment = false,
            };
            BusRecordConfigTable[BusChannelType.CanFD] = new BusRecordConfig
            {
                Format = BusRecordFormat.MultiTypeAsc,
                SmallSegment = false,
            };
            BusRecordConfigTable[BusChannelType.Lin] = new BusRecordConfig
            {
                Format = BusRecordFormat.MultiTypeBlf,
                SmallSegment = false,
            };
            BusRecordConfigTable[BusChannelType.Flexray] = new BusRecordConfig
            {
                Format = BusRecordFormat.MultiTypeBlf,
                SmallSegment = false,
            };
            BusRecordConfigTable[BusChannelType.Ethernet] = new BusRecordConfig
            {
                Format = BusRecordFormat.MultiTypeBlf,
                SmallSegment = true,
            };
            BusRecordConfigTable[BusChannelType.SomeIP] = new BusRecordConfig
            {
                Format = BusRecordFormat.MultiTypeBlf,
                SmallSegment = true,
            };

            RecordLegacyStyle = false;
        }

        public static DefaultFileIOConfig Load(object caller)
        {
            var config = new DefaultFileIOConfig();
            var configString = Agency.GetModuleConfig(caller, "default-file-io.plugindefaultfileio");
            if (configString == null) return null;
            config.SetConfig(configString);
            return config;
        }

        public void Save(object caller)
        {
            Agency.SetModuleConfig(caller, "default-file-io.plugindefaultfileio", GetConfig());
        }

        public override string GetConfig()
        {
            var xml = Xml.Create("root");
            var cw = new AttributeWriter(xml.DocumentElement);

            cw.WriteBool("online_write_bus", OnlineWriteBus, "yes", "no");
            cw.WriteInt("online_write_signal", (int)OnlineWriteSignal);
            cw.WriteBool("online_write_sample", OnlineWriteSample, "yes", "no");
            cw.WriteBool("online_write_matrix", OnlineWriteMatrix, "yes", "no");
            cw.WriteInt("offline_write_signal", (int)OfflineWriteSignal);
            cw.WriteBool("offline_write_sample", OfflineWriteSample, "yes", "no");
            cw.WriteBool("offline_write_matrix", OfflineWriteMatrix, "yes", "no");

            cw.WriteBool("from_raw_read_bus", FromRawReadBus, "yes", "no");
            cw.WriteBool("offline_from_gen_read_bus", OfflineFromGenReadBus, "yes", "no");
            cw.WriteInt("offline_from_gen_read_signal", (int)OfflineFromGenReadSignal);
            cw.WriteBool("offline_from_gen_read_sample", OfflineFromGenReadSample, "yes", "no");
            cw.WriteBool("offline_from_gen_read_matrix", OfflineFromGenReadMatrix, "yes", "no");
            cw.WriteBool("replay_from_gen_read_bus", ReplayFromGenReadBus, "yes", "no");
            cw.WriteInt("replay_from_gen_read_signal", (int)ReplayFromGenReadSignal);
            cw.WriteBool("replay_from_gen_read_sample", ReplayFromGenReadSample, "yes", "no");
            cw.WriteBool("replay_from_gen_read_matrix", ReplayFromGenReadMatrix, "yes", "no");

            foreach (var pair in BusRecordConfigTable)
            {
                var prefix = pair.Key.ToString().ToLower() + "_";
                cw.WriteInt(prefix + "rec_format", (int)pair.Value.Format);
                cw.WriteBool(prefix + "small_seg", pair.Value.SmallSegment, "yes", "no");
            }

            cw.WriteBool("record_legacy_style", RecordLegacyStyle, "yes", "no");

            return xml.InnerXml;
        }

        public override void SetConfig(string config)
        {
            if (String.IsNullOrEmpty(config)) return;

            var xml = new XmlDocument();
            xml.LoadXml(config);

            var cp = new AttributeParser(xml.DocumentElement);

            OnlineWriteBus = cp.ParseBool("online_write_bus", "yes", true);
            OnlineWriteSignal = (SignalIOMode)cp.ParseInt("online_write_signal", (int)SignalIOMode.WithoutBusSignals);
            OnlineWriteSample = cp.ParseBool("online_write_sample", "yes", true);
            OnlineWriteMatrix = cp.ParseBool("online_write_matrix", "yes", false);
            OfflineWriteSignal = (SignalIOMode)cp.ParseInt("offline_write_signal", (int)SignalIOMode.WithoutBusSignals);
            OfflineWriteSample = cp.ParseBool("offline_write_sample", "yes", true);
            OfflineWriteMatrix = cp.ParseBool("offline_write_matrix", "yes", false);

            FromRawReadBus = cp.ParseBool("from_raw_read_bus", "yes", true);
            OfflineFromGenReadBus = cp.ParseBool("offline_from_gen_read_bus", "yes", false);
            OfflineFromGenReadSignal = (SignalIOMode)cp.ParseInt("offline_from_gen_read_signal", (int)SignalIOMode.NoSignals);
            OfflineFromGenReadSample = cp.ParseBool("offline_from_gen_read_sample", "yes", true);
            OfflineFromGenReadMatrix = cp.ParseBool("offline_from_gen_read_matrix", "yes", false);
            ReplayFromGenReadBus = cp.ParseBool("replay_from_gen_read_bus", "yes", true);
            ReplayFromGenReadSignal = (SignalIOMode)cp.ParseInt("replay_from_gen_read_signal", (int)SignalIOMode.AllSignals);
            ReplayFromGenReadSample = cp.ParseBool("replay_from_gen_read_sample", "yes", true);
            ReplayFromGenReadMatrix = cp.ParseBool("replay_from_gen_read_matrix", "yes", true);

            foreach (var type in BusRecordConfigTable.Keys)
            {
                var prefix = type.ToString().ToLower() + "_";
                int formatID = cp.ParseInt(prefix + "rec_format", 0);
                if (formatID == 0) continue;

                var format = (BusRecordFormat)formatID;
                if (!availableRecordFormatTable.ContainsKey(format)) continue;
                if (!availableRecordFormatTable[format].Contains(type)) continue;

                BusRecordConfigTable[type].Format = format;
                BusRecordConfigTable[type].SmallSegment = cp.ParseBool(prefix + "small_seg", "yes", false);
            }

            RecordLegacyStyle = cp.ParseBool("record_legacy_style", "yes", false);
        }

        public override void DisableAll()
        {
            OnlineWriteBus = false;
            OnlineWriteSignal = SignalIOMode.NoSignals;
            OnlineWriteSample = false;
            OnlineWriteMatrix = false;
            OfflineWriteSignal = SignalIOMode.NoSignals;
            OfflineWriteSample = false;
            OfflineWriteMatrix = false;

            FromRawReadBus = false;
            OfflineFromGenReadBus = false;
            OfflineFromGenReadSignal = SignalIOMode.NoSignals;
            OfflineFromGenReadSample = false;
            OfflineFromGenReadMatrix = false;
            ReplayFromGenReadBus = false;
            ReplayFromGenReadSignal = SignalIOMode.NoSignals;
            ReplayFromGenReadSample = false;
            ReplayFromGenReadMatrix = false;
        }

        public override ConfigStatus GetConfigStatus()
        {
            switch (Agency.GetAppMode())
            {
            case ApplicationMode.Online:
            case ApplicationMode.Remote:
                if (OnlineWriteBus) return ConfigStatus.Enabled;
                if (OnlineWriteSignal != SignalIOMode.NoSignals) return ConfigStatus.Enabled;
                if (OnlineWriteSample) return ConfigStatus.Enabled;
                if (OnlineWriteMatrix) return ConfigStatus.Enabled;
                break;
            case ApplicationMode.Offline:
                if (OfflineWriteSignal != SignalIOMode.NoSignals) return ConfigStatus.Enabled;
                if (OfflineWriteSample) return ConfigStatus.Enabled;
                if (OfflineWriteMatrix) return ConfigStatus.Enabled;
                if (FromRawReadBus) return ConfigStatus.Enabled;
                if (OfflineFromGenReadBus) return ConfigStatus.Enabled;
                if (OfflineFromGenReadSignal != SignalIOMode.NoSignals) return ConfigStatus.Enabled;
                if (OfflineFromGenReadSample) return ConfigStatus.Enabled;
                if (OfflineFromGenReadMatrix) return ConfigStatus.Enabled;
                break;
            case ApplicationMode.Replay:
                if (FromRawReadBus) return ConfigStatus.Enabled;
                if (ReplayFromGenReadBus) return ConfigStatus.Enabled;
                if (ReplayFromGenReadSignal != SignalIOMode.NoSignals) return ConfigStatus.Enabled;
                if (ReplayFromGenReadSample) return ConfigStatus.Enabled;
                if (ReplayFromGenReadMatrix) return ConfigStatus.Enabled;
                break;
            }
            return ConfigStatus.Disabled;
        }

        public override List<RecordDataType> GetRecordDataTypes()
        {
            var list = new List<RecordDataType>();
            switch (Agency.GetAppMode())
            {
            case ApplicationMode.Online:
            case ApplicationMode.Remote:
                if (OnlineWriteBus) list.Add(RecordDataType.BusRawData);
                if (OnlineWriteSignal != SignalIOMode.NoSignals) list.Add(RecordDataType.Signals);
                if (OnlineWriteSample) list.Add(RecordDataType.Samples);
                if (OnlineWriteMatrix) list.Add(RecordDataType.Matrices);
                break;
            case ApplicationMode.Offline:
                if (OfflineWriteSignal != SignalIOMode.NoSignals) list.Add(RecordDataType.Signals);
                if (OfflineWriteSample) list.Add(RecordDataType.Samples);
                if (OfflineWriteMatrix) list.Add(RecordDataType.Matrices);
                break;
            }
            return list;
        }
    }
}